<template>
  <div class="allocResoure_container">
    <!-- 29.62循环渲染list -->
    <div class="allocResoure_box" v-for="parent in list" :key="parent.id">
      <!-- 29.55使用 element-plus 里面的 Checkbox 多选框---中间状态 -->
      <!-- 父级 -->
      <!-- 29.56复制以下代码 -->
      <!-- 29.57这里渲染的就是一级资源的列表,这里的v-model我们都要去放进去obj里面 -->
      <!-- <el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">Check all</el-checkbox> -->
      <!-- 29.63渲染父级的资源/一级资源 -->
      <!-- <el-checkbox v-model="parent.checkAll" :indeterminate="parent.isIndeterminate">{{ parent.name }}</el-checkbox> -->
      <!-- 29.67添加点击一级资源的事件,这里使用该组件的方法 change -->
      <el-col class="checkbox">
        <el-checkbox v-model="parent.checkAll" :indeterminate="parent.isIndeterminate" @change="checkedAll(parent)">{{
          parent.name }}</el-checkbox>
      </el-col>
      <!-- 子级 -->
      <!-- 29.64渲染子级的资源/二级资源 -->
      <!-- <el-checkbox-group v-model="parent.checkedIds"> -->
      <!-- 29.74给二级资源绑定点击事件,这里使用该组件的方法 change -->
      <el-checkbox-group v-model="parent.checkedIds" @change="checkedChild(parent)">
        <!-- <el-checkbox v-for="child in parent.children" :key="child.id" :label="child.name">{{
        child.name
      }}</el-checkbox> -->
        <!-- 29.65修改子级资源的结构 -->
        <el-row class="checkboxgroup">
          <!-- 29.66修改span，span一共为24份，这里每行三份，就是一列占span=8 -->
          <el-col :span="8" v-for="child in parent.children" :key="child.id">
            <!-- 29.89将 child.name 改成 child.id，通过id获取，而不是name -->
            <!-- <el-checkbox :label="child.name">{{ -->
            <el-checkbox :label="child.id">{{
              child.name
            }}</el-checkbox>
          </el-col>
        </el-row>
      </el-checkbox-group>
    </div>

    <!-- 29.90绑定点击的保存事件 -->
    <div class="button_btns">
      <el-button type="primary" @click="submitFn">保存</el-button>
      <!-- 29.104绑定点击清空按钮 -->
      <el-button @click="cleanFn">清空</el-button>
    </div>
  </div>
</template>
<script setup lang='ts'>
import { ApiGetResourceCategory, ApiGetResourceListAll, ApiGetRoleListResource, ApiChangeAllocResource } from '@/http/api';
// @ts-ignore
import { ElMessageBox, ElMessage } from 'element-plus';
import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';

// 二十九、点击分配资源
// 29.1去到 http 文件夹 api.ts 里面发送请求

// 29.5因为我们需要获取当前路由的id
const route = useRoute()
const router = useRouter()

// 29.88隐藏以下代码
// 29.25定义三个响应式数据去存接口返回的数据
// 29.36隐藏以下 allCategory 和 resources 的响应式数组
// 一级资源
// const allCategory = ref<IResourceCategoryModel[]>([])
// 二级资源
// const resources = ref<IResourceModel[]>([])

// 当前角色的资源
// const roleResources = ref<IResourceModel[]>([])

// 29.47构建一个满足父子关系的类型约束
// type IListParentWithChild = IResourceCategoryModel & { children: IResourceModel[] }
// 29.58把v-model需要的都放进来
// type IListParentWithChild = IResourceCategoryModel & { children: IResourceModel[], checkAll: boolean, isIndeterminate: boolean, checkedIds: any[] }
// 29.107一开始不知道 checkedIds 是什么类型的数组,用了 any 来约束,现在改回是 number
type IListParentWithChild = IResourceCategoryModel & { children: IResourceModel[], checkAll: boolean, isIndeterminate: boolean, checkedIds: number[] }
// 29.48定义list响应式数组，来存最后我们生成的一级二级资源的父子关系列表
let list = ref<IListParentWithChild[]>([])

// 29.37要把上面 allCategory一级资源 resources二级资源 的两个数组组成父子关系，使用双层 v-for 渲染到页面上，所有上面的代码可以不要了，请求都是异步的，实现父子关系的时候，是否需要两个数据都请求回来？是的！！！！
// 29.38如何保证两个数据都请求回来？Promise.all
// Promise.all([ApiGetResourceCategory(), ApiGetResourceListAll()]).then(res => {
// 29.79将当前角色的资源接口也放到这里
Promise.all([ApiGetResourceCategory(), ApiGetResourceListAll(), ApiGetRoleListResource(route.query.id as any as number)]).then(res => {
  // 29.41将res的数据解构出来
  // const [resourceCategoryRes, resourceListAllRes] = res;
  // 29.80将当前角色的资源接口，将res解构出来
  const [resourceCategoryRes, resourceListAllRes, roleResourcesRes] = res;
  // 29.42如果两个都同时请求成功才将数据赋值
  // if (resourceCategoryRes.code === 200 && resourceListAllRes.code === 200) {
  // 29.81需要三个接口成功请求
  if (resourceCategoryRes.code === 200 && resourceListAllRes.code === 200 && roleResourcesRes.code === 200) {
    // 29.43才构建父子关系，不然如果两个接口请求的速度不一致，就无法生成构建遍历
    // 29.44先遍历一级的资源数据
    resourceCategoryRes.data.forEach(parent => {
      // 29.45将二级的资源数据，定义属性，合并到一级资源菜单
      // 29.46约束 obj 的类型
      // let obj = {
      // 29.49约束 obj 返回的类型
      // let obj: IListParentWithChild = {
      //   ...parent,
      //   children: []
      // }
      // 29.59这边把新添加v-model需要的属性值也放到这里
      let obj: IListParentWithChild = {
        ...parent,
        children: [],
        // 29.60一开始默认值为false未选中
        checkAll: false,
        isIndeterminate: false,
        // 29.61子级选中的所有状态也是一个空数组
        checkedIds: []
      }
      // 29.50将obj追加进去
      list.value.push(obj)

      // 29.51遍历二级的资源数据
      resourceListAllRes.data.forEach(child => {
        // 29.52如果二级的资源数据的父级id与一级资源的id相等，那么该二级资源就是归属一级资源的子菜单
        if (child.categoryId === parent.id) {
          // 29.53将child追加到一级菜单的children属性里面
          obj.children.push(child)
        }
      })
    })
    // 29.54打印查看list是否生成了两级的数据
    // console.log(list.value);

    // 29.82把以及资源的 checkedIds 数组，根据角色拥有资源，设置一下
    roleResourcesRes.data.forEach(item => {
      // 29.83将 list 里面的值遍历
      list.value.forEach(subitem => {
        // 29.84判断当前选中状态返回的id跟 list里面的id有没有相同的
        if (subitem.id === item.categoryId) {
          // 29.85将当前的 id 追加到子资源的选中列表里面
          subitem.checkedIds.push(item.id)
        }
        // 29.86全选状态
        subitem.checkAll = subitem.checkedIds.length === subitem.children.length;
        // 29.87半选状态
        subitem.isIndeterminate = subitem.checkedIds.length > 0 && subitem.checkedIds.length < subitem.children.length;
      })
    })
  }
})

// 29.78需要三次请求都成功之后，才做数据处理，将以下代码隐藏
// 29.39将 ApiGetResourceCategory 和 ApiGetResourceListAll 的接口隐藏，放到 Promise.all里面
// 29.18发送接口请求数据，获取 查询所有后台资源分类
// 所有一级资源
// 29.19去到接口 types 文件夹里面 api.d.ts
// ApiGetResourceCategory();
// 29.26接口返回数据赋值
// ApiGetResourceCategory().then(res => {
//   if (res.code === 200) {
//     // 29.27查看接口返回的数据
//     // console.log(res.data);
//     // 29.28将数据赋值给定义的响应式数组
//     allCategory.value = res.data
//     // 29.35打印查看了值
//     console.log(allCategory.value);

//   }
// });

// 29.40将 ApiGetResourceCategory 和 ApiGetResourceListAll 的接口隐藏，放到 Promise.all里面
// 29.6发送接口请求数据，查询所有后台资源 跟 获取当前角色相关资源 两个接口
// 所有二级资源
// ApiGetResourceListAll();
// 29.29接口返回数据赋值
// ApiGetResourceListAll().then(res => {
//   if (res.code === 200) {
//     // 29.30查看接口返回的数据
//     // console.log(res.data);
//     // 29.31将数据赋值给定义的响应式数组
//     resources.value = res.data
//     // console.log(resources.value);

//   }
// });

// 29.7这里需要把id传进去
// ApiGetRoleListResource();
// 29.8去到 role.vue 文件，点击分配资源按钮的时候，需要把路径跳转到 allocResource.vue 文件
// 根据角色id获取当前角色所拥有的资源
// ApiGetRoleListResource(route.query.id as any as number);
// 29.32接口返回数据赋值
/* ApiGetRoleListResource(route.query.id as any as number).then(res => {
  if (res.code === 200) {
    // 29.33查看接口返回的数据
    // console.log(res.data);
    // 29.34将数据赋值给定义的响应式数组
    roleResources.value = res.data
    // console.log(roleResources.value);
  }
}); */

// 29.11刷新页面查看到当前的 listAll 接口请求，然后复制data的数据，去到 api.d.ts 约束接口返回的data类型约束

// 29.68定义全选/一级资源的事件
// const checkedAll = (item) => {
// 29.72约束 item 返回值的类型，那么 item 跟 childAll 就不会报错了
const checkedAll = (item: IListParentWithChild) => {
  // 29.69点的谁，就要把自己的状态变成和我一样===点击全选，以下所有选择框都勾选上
  let checkedAllBtn = item.checkAll;
  // 29.70如果全选上
  if (checkedAllBtn) {
    // 29.71把所有的子级菜单全选上
    // item.checkedIds = item.children.map(child => child.name)
    // 将 name 改成遍历 id
    item.checkedIds = item.children.map(child => child.id)
  } else {
    // 29.73全不选的时候就为空
    item.checkedIds = []
  }
}

// 29.75点击二级资源按钮事件
const checkedChild = (item: IListParentWithChild) => {
  // 29.76二级资源在点击的时候，还可能会是全选的情况（如果子级菜单所有选中的状态长度 等于 所有还是的长度）
  item.checkAll = item.checkedIds.length === item.children.length

  // 29.77判断所有的二级菜单是否勾选大于0，小于长度 
  item.isIndeterminate = item.checkedIds.length > 0 && item.checkedIds.length < item.children.length
}

// 29.91点击保存按钮事件
// 29.92发送接口请求,点击保存的时候会修改掉数据,去到 http 文件夹里面 api.ts 
const submitFn = () => {
  // 发送请求,请求要求携带的参数: resourceIds -- 勾选的资源id 和 roleId --- 当前的角色的id ---route.query.id

  // 29.97获取到当前用户选择的子集菜单勾选的状态
  // let ids = list.value.map(item => item.checkedIds)

  // 29.108这里可以使用迭代器去计算 checkedIds 的数组--方法一
  // let ids = list.value.reduce((prev: number[], current) => {
  // 29.109约束 prev 的类型方法一是上面的,方法二是用泛型约束
  let ids = list.value.reduce<number[]>((prev, current) => {
    prev.push(...current.checkedIds)
    return prev;
  }, [])

  // 方法二:
  // let ids: number[] = []
  // list.value.forEach(item => {
  //   item.checkedIds.forEach(subitem => {
  //     ids.push(subitem)
  //   })
  // })

  // 29.98查看是否能够获取，这里获取的ids是二维的数组
  console.log(ids);


  // 29.101弹框dialog提示用户是否删除
  ElMessageBox.confirm('此操作将修改当前用户资源', '是否继续', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
  }).then(() => {
    // 29.99请求接口
    ApiChangeAllocResource({
      // 29.100传递参数
      resourceIds: ids,
      roleId: route.query.id as any as number
    }).then(res => {
      if (res.code === 200) {
        // 29.102弹出提示框
        ElMessage({
          type: 'success',
          message: "修改成功"
        })
        // console.log(res);
        // 29.103路由跳转，返回上一级页面
        router.go(-1)

      }

    })
  })

}

// 29.105点击清空按钮事件
const cleanFn = () => {
  // console.log('点击清空');
  // 29.106将当前用户点击的子集菜单清空以及一级菜单的权限状态也改为false
  // console.log(list.value);
  list.value.forEach(item => {
    item.checkAll = false;  // 将 checkAll 属性设置为 false
    item.checkedIds.splice(0, item.checkedIds.length); // 清空 checkedIds 数组中的元素
  });
}


</script>
<style lang="less" scoped>
.allocResoure_container {
  padding: 20px;
  box-shadow: 0 0 4px 2px #eee;
  border-radius: 2px;

  .checkbox {
    padding: 20px;
    background-color: #f2f6fc;
  }

  .checkboxgroup {
    padding: 20px;
  }

  .button_btns {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 20px;
  }
}
</style>